基础概念
纯函数的定义是什么?为什么在函数式编程中纯函数很重要?
- 纯函数:相同输入永远返回相同输出,不依赖外部状态,也不产生副作用。
- 重要性:提高可预测性、可测试性、可复用性,便于并行计算。
解释什么是引用透明性,并给出 JavaScript 示例。
概念:表达式可以被它的值替换而不影响程序行为。
示例
javascriptconst add = (a, b) => a + b; const result = add(2, 3); console.log(result); // 5
不可变性在函数式编程中的作用?
- 保证数据不会被意外修改 → 降低副作用和调试难度。
- 例子:用 map 创建新数组,而不是修改原数组。
JavaScript 中函数是一等公民是什么意思?
函数可以像值一样:存变量、当参数、当返回值。
示例
javascriptlet log = fn => x => { console.log(x); return fn(x); }; // 等价 function log { return function(x) { console.log(x); return fn(x); } }
声明式 vs 命令式
命令式:告诉“怎么做”(for 循环)。
声明式:告诉“做什么”(map、filter)。
示例
javascript// 命令式 let numbers = [1, 2, 3, 4, 5]; let sum = 0; for (let i = 0; i < numbers.length; i++) { sum += numbers[i]; } // 声明式 let numbers = [1, 2, 3, 4, 5]; let sum = numbers.reduce((acc, curr) => acc + curr, 0);
解释什么是高阶函数,并给出 JavaScript 示例。
概念:接受函数作为参数或返回函数的函数。
示例
javascriptconst map = (arr, fn) => arr.map(fn); const numbers = [1, 2, 3, 4, 5]; const doubled = map(numbers, x => x * 2);
解释什么是柯里化,并给出 JavaScript 示例。
概念:将一个多参数函数转换成一系列单参数函数。
示例
javascriptconst curry = fn => (...args) => args.length >= fn.length ? fn(...args) : (...nextArgs) => curry(fn)(...args, ...nextArgs); const add = (a, b) => a + b; const curriedAdd = curry(add); const result = curriedAdd(2, 3);
解释什么是函数组合,并给出 JavaScript 示例。
概念:将多个函数组合成一个函数。
示例
javascriptconst compose = (f, g) => x => f(g(x)); const toUpperCase = x => x.toUpperCase(); const exclaim = x => x + '!'; const shout = compose(exclaim, toUpperCase); const result = shout('hello');
解释什么是函数式编程,并给出 JavaScript 示例。
手写柯里化 (Currying)
javascript
/* curry函数柯里化.js */
export function curry(fn) {
if (typeof fn !== 'function') {
throw new TypeError('Expected a function');
}
const arity = fn.length;
function curried(...args) {
if (args.length >= arity) {
return fn(...args);
} else {
return function (...nextArgs) {
return curried(...args, ...nextArgs);
};
}
}
return curried;
}
// 示例
const multiply = (a, b, c) => a * b * c;
const curriedMultiply = curry(multiply);
console.log(curriedMultiply(2)(3)(4)); // 24- 手写 Memoize 与 Compose
javascript
/* memoize和compose.js */
// Memoize (缓存)
export function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
}
}
// Compose (组合)
export function compose(...fns) {
return function(...args) {
// reduceRight 实现从右向左执行
// 注意:这里的简单实现假设第一个函数可以接收多个参数,后续函数接收单个参数
// 或者 args 在这里被当作一个整体传递(这取决于具体实现预期)
// 通常 compose: (...fns) => x => fns.reduceRight((acc, f) => f(acc), x)
return fns.reduceRight((acc, fn) => fn(acc), args);
}
}- 手写 call, apply, bind
javascript
/* 手写call、apply、bind.js */
// Call
Function.prototype.mycall = function (context, ...args) {
if (typeof this !== 'function') {
throw new TypeError('mycall must be called on a function')
}
context = context || globalThis
const fnSymbol = Symbol()
context[fnSymbol] = this
const result = context[fnSymbol](...args)
delete context[fnSymbol]
return result
}
// Apply
Function.prototype.myapply = function (context, args) {
if (typeof this !== 'function') {
throw new TypeError('myapply must be called a function')
}
context = context || globalThis
const fnSymbol = Symbol()
context[fnSymbol] = this
const result = Array.isArray(args) ? context[fnSymbol](...args) : context[fnSymbol]()
delete context[fnSymbol]
return result
}
// Bind
Function.prototype.mybind = function (context, ...args) {
if (typeof this !== "function") {
throw TypeError("myBind must be called on a function")
}
const self = this
const boundFn = function (...newArgs) {
// 处理 new 操作符的情况
const isNew = this instanceof boundFn
return self.apply(isNew ? this : context, args.concat(newArgs))
}
boundFn.prototype = Object.create(self.prototype)
return boundFn
}- 链式调用示例
javascript
/* 链式调用.js */
export class Chainable {
constructor(value = 0) {
this.value = value
}
add(val) {
this.value += val
return this
}
subtract(val) {
this.value -= val
return this
}
getValue() {
return this.value
}
}
const result = new Chainable(10).add(5).subtract(3).getValue()
console.log(result); // 12